package generator.workflow.sfj.gridgain;

import generator.datastructure.ForkPoint;
import generator.datastructure.MethodDeclaration;
import generator.datastructure.VarDeclaration;
import generator.datastructure.WritebleClass;
import generator.workflow.sfj.SFJPeerClassBuilder;

import java.util.List;

/**Esta clase instancia los pasos genéricos de gridificación para middlewares 
 * basados en futures de java.
 * En particular, instancia dichos pasos para el middleware de GridGain.
 * @author Hirsch-Fernández
 * */
public abstract class GridGainPeer implements SFJPeerClassBuilder {
	
	/**Devuelve una lista con los puntos de bifurcación contenidos dentro del método
	 * a gridificar. Estos puntos son aquellas referencias a las sentencias donde 
	 * existe un llamado recursivo cuyo resultado se asigna a una variable denominada
	 * variable grid.
	 */
	@Override
	public abstract List<ForkPoint> getForks(MethodDeclaration md);
	
	/** Método encargado de devolver la clase Peer modificada para la gridificación
	*/
	@Override	
	public abstract WritebleClass getPeerClass();
	
	/**{@inheritDoc}
	 * La creación y envío de un trabajo es realizada aplicando la siguiente transformación sobre
	 * la sentencia fork original:<P>
	 * Ejemplo de sentencia fork original:<BR>
	 * <b>this.fib(a);</b><BR>
	 * Ejemplo de sentencia fork gridificada:<BR>
	 * <b>grid.execute(new GridExecutorCallableTask(),new JobClass(this,a));</b><P>
	 * Donde GridExecutorCallableTask es una clase pertenciente al middleware de GridGain y JobClass será la clase creada
	 * por el builder {@link GridJob}<BR>
	 * A su vez, el resultado devuelto por la sentencia fork gridificada será asignado a una variable Future, en particular,
	 * a la implementación de Future que realiza el middleware de GridGain que es GridTaskFuture&lt;WrapperGR&gt; .
	 * Donde WrapperGR será la clase construida por el builder {@link GridResult}*/
	@Override
	public ForkPoint gridEnableFork(ForkPoint fork) {
		this.setImports();				
		this.declareFuture(fork);
		return this.submitJobOnGrid(fork);
	}

	/** Realiza la inserción de imports sobre la clase que contiene
	 * los métodos a ser gridificados. Estos imports son necesarios para insertar
	 * las instrucciones de gridificicación que hacen uso del middleware GridGain*/
	protected abstract void setImports();
	
	/** Realiza la declaración del objeto Future que alojará el resultado de la ejecución
	 * de un fork en el grid. Si el fork contiene la declaración de la variable grid (la que se supone
	 * alojará el resultado de la ejecución -convención de programación-), esta será reemplazada por la
	 * correspondiente declaración del future.
	 * Por el contrario, si el fork no contiene la declaración de la variable grid (sino una referencia
	 * a esta), la declaración del future deberá realizarse del siguiente modo: reemplazar la referencia
	 * a la variable grid por una referencia a la variable future y declarar el future de forma aislada
	 * en la línea inmediata posterior a donde se encuentre la declaración de la variable grid.
	 *  @param fork
	 * 			representa el llamado recursivo que pretende convertirse en un trabajo para que su
	 * 			ejecución pueda realizarse utilizando los servicios del middleware de GridGain
	 * */
	protected abstract void declareFuture(ForkPoint fork);

	/**Dada una sentencia fork convencional realiza todos los cambios necesarios para que el llamado
	 * recursivo represente un trabajo y a su vez éste sea enviado al grid para su ejecución.
	 *@param fork
	 * 			contiene el llamado recursivo que pretende convertirse en un trabajo para ser ejecutado
	 * 			en el grid.
	 * @return la sentencia fork gridificada.
	 */
	protected abstract ForkPoint submitJobOnGrid(ForkPoint fork);
	
	/**{@inheritDoc}*/
	@Override
	public VarDeclaration isolateGridVariableDeclaration(ForkPoint fork){
		this.createLocalExecutionMethod(fork);	
		return this.isolate(fork);
	}

	/**Realiza una clonación del método a gridificar antes de que este reciba la primer
	 * modificación correspondiente al proceso de gridificación. El nombre del clon 
	 * será el mismo que el original pero anexando el sufijo "_local". El clon será
	 * insertado como un nuevo método de la clase peer.*/
	protected abstract void createLocalExecutionMethod(ForkPoint fork);
	
	/** Basandose en el llamado recibido como parametro, devuelve la linea de delcaración de la variable
	 * a la que se le asigna el resultado recursivo, previo a la gridificación del fork (variable receptora original).
	 * En caso de que la linea de llamado que representa el fork sea tambien la linea de declaración, genera una nueva declaración
	 * de variable y la retorna
	 * @param fork
	 * @return declaración de la variable a la que se asigna el llamado recursivo.
	 */
	protected abstract VarDeclaration isolate(ForkPoint fork);

	/**{@inheritDoc}*/
	@Override
	public void setJoinPoints(VarDeclaration gridVariableDec,
			ForkPoint gridifiedFork) {
		this.replaceGridVarUses(gridVariableDec,gridifiedFork);
		this.insertGetMethod(gridifiedFork);
	}

	/**
	 * Dada la declaración de una variable, y el punto de fork donde se utiliza la misma, se reemplazarán sus posteriores
	 * usos para poder obtener su nuevo valor, el cual se ha calculado en el grid.
	 * @param gridVariableDec declaración de la variable que obtiene su valor del cálculo en el grid.
	 * @param gridifiedFork punto de Fork a partir del cual el valor de la variable es el calculado en el grid.
	 */
	protected abstract void replaceGridVarUses(VarDeclaration gridVariableDec,
			ForkPoint gridifiedFork);
	
	/**
	 * Se encargado de insertar el cuerpo del método encargado de realizar el submit de un nuevo trabajo
	 * o, en caso de no ser posible, realizar la ejecución del método a gridificar de forma local.
	 * Recibe como parametro el llamado fork que se ejecutara en el grid.
	 * @param gridifiedFork punto de fork a ejecutar en el grid
	 */
	protected abstract void insertGetMethod(ForkPoint gridifiedFork);
	

}
